package com.ztesoft.book.common.utils.encrypt;

import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;


/**
 * @Description AES 128bit 加密解密工具类
 * @Author YangFan
 * @Date 2021-06-22 17:20
 */
@Slf4j
public final class AesExtraEncrypUtil {


    private AesExtraEncrypUtil() {
    }

    private static final String S_KEY = "ZTESOFT2021_BOOK";

    /**
     * 加密解密类型
     */
    private static final String TRANSFORMATION = "AES/GCM/NoPadding";

    private static final String KEY_ALGORITHM = "AES";
    /**
     * 起始位置
     */
    private static final int START_NUM = 0;
    /**
     * 目标位置,gcm方式的向量iv长度
     */
    private static final int END_NUM = 12;
    /**
     * 扩展大小,生成的MAC
     */
    private static final int EXT_NUM = 16;
    /**
     * GCM 偏移量
     */
    private static final int GCM_SPE = 128;

    private static final String AES = "AES";

    private static SecretKey secretkey;

    private static GCMParameterSpec ivSpec;

    static {
        secretkey = new SecretKeySpec(S_KEY.getBytes(StandardCharsets.UTF_8), KEY_ALGORITHM);
    }


    /**
     * 方法功能描述:
     * < 加密方法 >
     *
     * @param data 【参数 1 注释说明】
     * @return String                        【返回值 注释说明】
     * @author YangFan
     * @date 2021-06-22 17:20
     */
    public static String encryptExt(String data) {

        try {
            //"算法/模式/补码方式"
            SecretKeySpec skeySpec = new SecretKeySpec(secretkey.getEncoded(), AES);
            byte[] bytes = S_KEY.getBytes(StandardCharsets.UTF_8);
            //GCM向量iv
            ivSpec = new GCMParameterSpec(GCM_SPE, bytes);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
            byte[] iv = cipher.getIV();
            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            byte[] message = new byte[END_NUM + data.getBytes(StandardCharsets.UTF_8).length + EXT_NUM];
            System.arraycopy(iv, START_NUM, message, START_NUM, END_NUM);
            System.arraycopy(encrypted, START_NUM, message, END_NUM, encrypted.length);
            //此处使用BASE64做转码功能，同时能起到2次加密的作用。
            return Base64.encodeBase64String(message);
        }
        catch (RuntimeException e) {
            return null;
        }
        catch (Exception e) {
            log.error("处理失败：", e);
            return null;
        }
    }

    /**
     * 方法功能描述:
     * < 解密方法 >
     *
     * @param data 【参数 1 注释说明】
     * @return String                        【返回值 注释说明】
     * @author YangFan
     * @date 2021-06-22 17:20
     */
    public static String desEncryptExt(String data) {
        try {
            byte[] encrypted = Base64.decodeBase64(data);
            byte[] bytes = S_KEY.getBytes(StandardCharsets.UTF_8);
            //GCM向量iv
            ivSpec = new GCMParameterSpec(GCM_SPE, bytes);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretkey, ivSpec);
            byte[] original = cipher.doFinal(encrypted, END_NUM, encrypted.length - END_NUM);
            String originalString = new String(original, StandardCharsets.UTF_8);
            if ("".equals(originalString)) {
                return data;
            }
            else {
                return originalString.trim();
            }
        }
        catch (RuntimeException e) {
            return null;
        }
        catch (Exception e) {
            log.error("处理失败：", e);
            return null;
        }
    }

}
